'use client';

import React from 'react';

/**
 * 比较函数类型
 */
export type CompareFunction<P extends object> = (prevProps: P, nextProps: P) => boolean;

/**
 * 默认比较函数
 * 浅比较所有属性
 */
function defaultCompare<P extends object>(prevProps: P, nextProps: P): boolean {
  if (Object.is(prevProps, nextProps)) {
    return true;
  }

  if (
    typeof prevProps !== 'object' || 
    prevProps === null || 
    typeof nextProps !== 'object' || 
    nextProps === null
  ) {
    return false;
  }

  const prevKeys = Object.keys(prevProps);
  const nextKeys = Object.keys(nextProps);

  if (prevKeys.length !== nextKeys.length) {
    return false;
  }

  return prevKeys.every(key => {
    return Object.prototype.hasOwnProperty.call(nextProps, key) &&
      Object.is(prevProps[key as keyof P], nextProps[key as keyof P]);
  });
}

/**
 * 创建一个记忆化的组件
 * 
 * @param Component 要记忆化的组件
 * @param compare 比较函数，用于确定是否重新渲染
 * @returns 记忆化的组件
 * 
 * @example
 * ```tsx
 * const MemoizedComponent = withMemo(ExpensiveComponent);
 * 
 * // 使用自定义比较函数
 * const MemoizedWithCustomCompare = withMemo(ExpensiveComponent, 
 *   (prev, next) => prev.id === next.id
 * );
 * ```
 */
export function withMemo<P extends object>(
  Component: React.ComponentType<P>,
  compare?: CompareFunction<P>
): React.MemoExoticComponent<React.ComponentType<P>> {
  const compareFunction = compare || defaultCompare;
  const displayName = Component.displayName || Component.name || 'Component';
  
  const MemoizedComponent = React.memo(Component, compareFunction);
  MemoizedComponent.displayName = `withMemo(${displayName})`;
  
  return MemoizedComponent;
}

/**
 * 创建一个记忆化的组件，只有在指定的属性变化时才重新渲染
 * 
 * @param Component 要记忆化的组件
 * @param propsToCompare 需要比较的属性名数组
 * @returns 记忆化的组件
 * 
 * @example
 * ```tsx
 * // 只有当id或name改变时才重新渲染
 * const MemoizedComponent = withMemoProps(UserProfile, ['id', 'name']);
 * ```
 */
export function withMemoProps<P extends object>(
  Component: React.ComponentType<P>,
  propsToCompare: (keyof P)[]
): React.MemoExoticComponent<React.ComponentType<P>> {
  const compareFunction = (prevProps: P, nextProps: P): boolean => {
    return propsToCompare.every(prop => 
      Object.is(prevProps[prop], nextProps[prop])
    );
  };
  
  const displayName = Component.displayName || Component.name || 'Component';
  
  const MemoizedComponent = React.memo(Component, compareFunction);
  MemoizedComponent.displayName = `withMemoProps(${displayName})`;
  
  return MemoizedComponent;
}

/**
 * 创建一个记忆化的组件，忽略指定的属性变化
 * 
 * @param Component 要记忆化的组件
 * @param propsToIgnore 需要忽略的属性名数组
 * @returns 记忆化的组件
 * 
 * @example
 * ```tsx
 * // 忽略style和className的变化
 * const MemoizedComponent = withMemoExcept(UserProfile, ['style', 'className']);
 * ```
 */
export function withMemoExcept<P extends object>(
  Component: React.ComponentType<P>,
  propsToIgnore: (keyof P)[]
): React.MemoExoticComponent<React.ComponentType<P>> {
  const compareFunction = (prevProps: P, nextProps: P): boolean => {
    const allKeys = new Set([
      ...Object.keys(prevProps),
      ...Object.keys(nextProps)
    ]) as Set<keyof P>;
    
    // 过滤掉要忽略的属性
    const keysToCompare = Array.from(allKeys).filter(
      key => !propsToIgnore.includes(key)
    );
    
    // 比较剩余属性
    return keysToCompare.every(key => 
      Object.is(prevProps[key], nextProps[key])
    );
  };
  
  const displayName = Component.displayName || Component.name || 'Component';
  
  const MemoizedComponent = React.memo(Component, compareFunction);
  MemoizedComponent.displayName = `withMemoExcept(${displayName})`;
  
  return MemoizedComponent;
} 